#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <queue>

void lock();
void unlock();
void wait();
void notifyAll();
void notify();
void* run(void*);
void thprintln(const char* msg, int error = 0);

static const int limit = 100;
static pthread_t threads[limit];
static pthread_cond_t queue_condition;
static pthread_mutex_t mutex;
static bool running;

class Job {
    public:
        void execute() {
	    thprintln("I'm a job!");
	}
};

void schedule(Job*);
Job* next();

static std::queue<Job*> job_queue;

int main(int argc, char *argv[])
{
/*    size_t stacksize;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_getstacksize (&attr, &stacksize);
    printf("Default stack size = %d\n", stacksize);		         
    printf("PTHREAD_STACK_MIN = %d\n", PTHREAD_STACK_MIN);
*/
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&queue_condition, NULL);
    running = true;
    int pool_size = 0;
    
    thprintln("main: init");
    lock();
    while(pool_size<limit) {
        int error = pthread_create(&threads[pool_size++], NULL, &run, NULL); 
	thprintln("thread created", error);
    }
    unlock();
    thprintln("init done.");

    sleep(2);
    schedule(new Job());
    sleep(2);
}

void* run(void*) {
    while (running) {
        lock();
        Job* job = next();
        if (job == NULL) {
	    thprintln("next job is NULL");
            wait();
            unlock();
            continue;
        }
	thprintln("next job OK!");
        unlock();
        job->execute();
        delete job;
    }
}

void lock() {
    thprintln("locking...");
    int error = pthread_mutex_lock(&mutex);
    thprintln("locked", error);
}

void unlock() {
    thprintln("unlocking...");
    int error = pthread_mutex_unlock(&mutex);
    thprintln("unlocked", error);
}

void wait() {
    thprintln("waiting...");
    int error = pthread_cond_wait(&queue_condition, &mutex);
    thprintln("awaken!", error);
}

void notifyAll() {
    thprintln("notifyAll...");
    int error = pthread_cond_broadcast(&queue_condition);
    thprintln("all notified", error);
}

void notify() {
    thprintln("notify...");
    int error = pthread_cond_signal(&queue_condition);
    thprintln("notified", error);
}

void schedule(Job* job) {
    lock();
    job_queue.push(job);
    thprintln("job scheduled.");
    notify();
    unlock();
}

Job* next() {
    if (job_queue.empty()) {
        return NULL;
    }
    Job* job = job_queue.front();
    job_queue.pop();
    return job;
}

void thprintln(const char* msg, int error) {
    if (error) {
        printf("%u] %s\t error=%i\n", pthread_self(), msg, error);
    } else {
        printf("%u] %s\n", pthread_self(), msg);
    }   
}
